了解MySQL重写批处理语句(rewriteBatchedStatements)

您所在的位置:网站首页 jdbc executebatch 了解MySQL重写批处理语句(rewriteBatchedStatements)

了解MySQL重写批处理语句(rewriteBatchedStatements)

2024-01-13 15:53| 来源: 网络整理| 查看: 265

MySQL重写BatchStatements

想象一下,有一个工具可以自动检测JPA和Hibernate的性能问题。这难道不是很好吗?

好吧,Hypersistence Optimizer就是这样的工具!而且它可以与Spring Boot、Spring Framework、Jakarta EE、Java EE、Quarkus或Play Framework一起使用。

因此,请享受将你的时间花在你喜欢的事情上,而不是在周六晚上修复你的生产系统的性能问题!

简介

在这篇文章中,我们将看到在使用JDBC、JPA或Hibernate时,MySQL rewriteBatchedStatements如何工作。

我第一次研究这个MySQL配置属性是在我写[高性能Java持久性]一书的批处理章节时,那时,我发现这个设置允许通过重写发送到数据库的SQL字符串来批处理普通Statement 。

然而,[MySQL 6 Connector/J的文档]中提到,

对于准备好的语句,服务器端准备好的语句目前不能利用这个重写选项

所以,在很长一段时间里,我错误地认为这个功能不是为了批量处理JDBC准备好的语句。

当我读到MySQL 8.0.30 Connector/J发布说明时,我才意识到文档一直在误导我们。

连接属性rewriteBatchedStatements 的描述已被纠正,删除了服务器端准备好的语句不能利用重写选项的限制。(Bug #34022110)

所以,很明显,rewriteBatchedStatements 是与JDBCPreparedStatements一起工作的,为此,我决定测试这个功能,并将我的发现写在这篇文章中。

在JDBC语句批处理中使用rewriteBatchedStatements

大多数Java开发人员在需要执行INSERT、UPDATE和DELETE语句时,会使用 executeUpdate当需要执行INSERT、UPDATE和DELETE语句时,Statement 接口的方法。

然而,从Java 1.2开始,Statement 接口就提供了addBatch ,我们可以用它来批处理多个语句,以便在调用executeBatch 方法时在一个请求中发送这些语句,如下例所示。

String INSERT = "insert into post (id, title) values (%1$d, 'Post no. %1$d')"; try(Statement statement = connection.createStatement()) { for (long id = 1; id 4) { return executeBatchUsingMultiQueries( multiQueriesEnabled, nbrCommands, individualStatementTimeout ); } updateCounts = new long[nbrCommands]; for (int i = 0; i < nbrCommands; i++) { updateCounts[i] = -3; } int commandIndex = 0; for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { try { String sql = (String) batchedArgs.get(commandIndex); updateCounts[commandIndex] = executeUpdateInternal(sql, true, true); ... } catch (SQLException ex) { updateCounts[commandIndex] = EXECUTE_FAILED; ... } }

false因为rewriteBatchedStatements ,每个INSERT语句将使用executeUpdateInternal 方法调用单独执行。

因此,即使我们使用了addBatch 和executeBatch ,在默认情况下,MySQL在使用普通的JDBCStatement 对象时仍然会单独执行INSERT语句。

然而,如果我们启用rewriteBatchedStatements JDBC配置属性。

MysqlDataSource dataSource = new MysqlDataSource(); String url = "jdbc:mysql://localhost/high_performance_java_persistence?useSSL=false"; dataSource.setURL(url); dataSource.setUser(username()); dataSource.setPassword(password()); dataSource.setRewriteBatchedStatements(true);

并调试executeBatch 方法的执行,你会看到,现在,executeBatchUsingMultiQueries 被调用。

if (this.rewriteBatchedStatements.getValue() && nbrCommands > 4) { return executeBatchUsingMultiQueries( multiQueriesEnabled, nbrCommands, individualStatementTimeout ); }

而executeBatchUsingMultiQueries 方法将把各个INSERT语句串联成一个StringBuilder ,并运行一个execute 的调用。

StringBuilder queryBuf = new StringBuilder(); batchStmt = locallyScopedConn.createStatement(); JdbcStatement jdbcBatchedStmt = (JdbcStatement) batchStmt; ... int argumentSetsInBatchSoFar = 0; for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { String nextQuery = (String) this.query.getBatchedArgs().get(commandIndex); ... queryBuf.append(nextQuery); queryBuf.append(";"); argumentSetsInBatchSoFar++; } if (queryBuf.length() > 0) { try { batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS); } catch (SQLException ex) { sqlEx = handleExceptionForBatch( commandIndex - 1, argumentSetsInBatchSoFar, updateCounts, ex ); } ... }

因此,对于普通的JDBCStatement 批处理,MySQLrewriteBatchedStatements 配置属性将附加当前批处理的语句并在单个数据库往返中执行它们。

在JDBC预处理语句批处理中使用rewriteBatchedStatements

当使用JPA和Hibernate时,你所有的SQL语句都将使用JDBCPreparedStatement 来执行,这是很好的理由。

预备语句允许你增加语句缓存的可能性 准备好的语句允许你避免SQL注入攻击,因为你绑定了参数值,而不是像我们在之前的String.format 调用中那样注入它们。

然而,由于Hibernate默认不启用JDBC批处理,我们需要提供以下配置属性来激活自动批处理机制。

spring.jpa.properties.hibernate.jdbc.batch_size=10; spring.jpa.properties.hibernate.order_inserts=true; spring.jpa.properties.hibernate.order_updates=true;

因此,当持久化10个Post 实体时。

for (long i = 1; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3